/*++

    Copyright (c) Microsoft Corporation.
    Licensed under the MIT License.

--*/

//
// Represents the Listener specific state.
//
typedef struct QUIC_LISTENER {

#ifdef __cplusplus
    struct QUIC_HANDLE _;
#else
    struct QUIC_HANDLE;
#endif

    //
    // Indicates the listener is listening on a wildcard address (v4/v6/both).
    //
    BOOLEAN WildCard;

    //
    // Indicates the listener has called ListenerClose.
    //
    BOOLEAN AppClosed;

    //
    // Indicates the listener is completely stopped.
    //
    BOOLEAN Stopped;

    //
    // Indicates the listener needs its stop complete event.
    //
    BOOLEAN NeedsStopCompleteEvent;

    //
    // Indicates the listener opted in for DoS Mode event.
    //
    BOOLEAN DosModeEventsEnabled;

    //
    // Indicates a DoS Mode event indication is required; cannot share a
    // bitfield due to atomic update.
    //
    BOOLEAN NeedsDosModeModeEvent;

    //
    // Tracks the most recent DoS Mode event state. Uses best-effort
    // synchronization.
    //
    BOOLEAN DosModeEnabled;

    //
    // Indicates the listener is constrained to a specific partition.
    //
    BOOLEAN Partitioned : 1;

    //
    // The thread ID that the listener is actively indicating a stop compelete
    // callback on.
    //
    CXPLAT_THREAD_ID StopCompleteThreadID;

    //
    // The link in the binding's list of listeners.
    //
    CXPLAT_LIST_ENTRY Link;

    //
    // The top level registration.
    //
    QUIC_REGISTRATION* Registration;

    //
    // Link into the registrations's list of listeners.
    //
    CXPLAT_LIST_ENTRY RegistrationLink;

    //
    // The listener worker.
    //
    QUIC_WORKER* Worker;

    //
    // Link into the worker.
    //
    CXPLAT_LIST_ENTRY WorkerLink;

    //
    // Indicates whether a worker is currently processing a listener.
    // N.B. Multi-threaded access, synchronized by worker's listener lock.
    //
    BOOLEAN WorkerProcessing : 1;
    BOOLEAN HasQueuedWork : 1;

#ifdef QUIC_SILO
    //
    // The silo.
    //
    QUIC_SILO Silo;
#endif

    //
    // Active reference count on the listener preventing a stop completion.
    //
    CXPLAT_REF_COUNT StartRefCount;

    //
    // Internal reference count on the listener preventing cleanup.
    //
    CXPLAT_REF_COUNT RefCount;

    //
    // Event to signal when the listener is stopped.
    //
    CXPLAT_EVENT StopEvent;

    //
    // The address that the listener is listening on.
    //
    QUIC_ADDR LocalAddress;

    //
    // The UDP binding this Listener is associated with.
    //
    QUIC_BINDING* Binding;

    //
    // The handler for the API client's callbacks.
    //
    QUIC_LISTENER_CALLBACK_HANDLER ClientCallbackHandler;

    //
    // Stats for the Listener.
    //
    uint64_t TotalAcceptedConnections;
    uint64_t TotalRejectedConnections;

    //
    // The application layer protocol negotiation buffers. Encoded in the TLS
    // extension format.
    //
    uint16_t AlpnListLength;
    _Field_size_(AlpnListLength)
    uint8_t* AlpnList;

    //
    // An app configured prefix for all connection IDs in this listener. The
    // first byte indicates the length of the ID, the second byte the offset of
    // the ID in the CID and the rest payload of the identifier.
    //
    uint8_t CibirId[2 + QUIC_MAX_CIBIR_LENGTH];

    //
    // An optional app-configured partition index for the listener and its
    // connections.
    //
    uint16_t PartitionIndex;
} QUIC_LISTENER;

#ifdef QUIC_SILO

#define QuicListenerAttachSilo(Listener) \
    QUIC_SILO PrevSilo = Listener->Silo == NULL ? \
        QUIC_SILO_INVALID : QuicSiloAttach(Listener->Silo)

#define QuicListenerDetachSilo() \
    if (PrevSilo != QUIC_SILO_INVALID) {\
        QuicSiloDetatch(PrevSilo); \
    }

#else

#define QuicListenerAttachSilo(Listener)
#define QuicListenerDetachSilo()

#endif // #ifdef QUIC_SILO

//
// Tracing rundown for the binding.
//
_IRQL_requires_max_(DISPATCH_LEVEL)
void
QuicListenerTraceRundown(
    _In_ QUIC_LISTENER* Listener
    );

_IRQL_requires_max_(DISPATCH_LEVEL)
void
QuicListenerStartReference(
    _In_ QUIC_LISTENER* Listener
    );

//
// Releases an active reference on the listener.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicListenerStartRelease(
    _In_ QUIC_LISTENER* Listener,
    _In_ BOOLEAN IndicateEvent
    );

    _IRQL_requires_max_(DISPATCH_LEVEL)
void
QuicListenerReference(
    _In_ QUIC_LISTENER* Listener
    );

_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicListenerRelease(
    _In_ QUIC_LISTENER* Listener
    );

//
// Returns TRUE if the two listeners have an overlapping ALPN.
//
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN
QuicListenerHasAlpnOverlap(
    _In_ const QUIC_LISTENER* Listener1,
    _In_ const QUIC_LISTENER* Listener2
    );

//
// Returns TRUE if the listener has a matching ALPN. Also updates the new
// connection info with the matching ALPN.
//
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN
QuicListenerMatchesAlpn(
    _In_ const QUIC_LISTENER* Listener,
    _In_ QUIC_NEW_CONNECTION_INFO* Info
    );

//
// Passes the connection to the listener to (possibly) accept it.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicListenerAcceptConnection(
    _In_ QUIC_LISTENER* Listener,
    _In_ QUIC_CONNECTION* Connection,
    _In_ const QUIC_NEW_CONNECTION_INFO* Info
    );

//
// Sets a Listener parameter.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
QuicListenerParamSet(
    _In_ QUIC_LISTENER* Listener,
    _In_ uint32_t Param,
    _In_ uint32_t BufferLength,
    _In_reads_bytes_(BufferLength)
        const void* Buffer
    );

//
// Gets a Listener parameter.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
QuicListenerParamGet(
    _In_ QUIC_LISTENER* Listener,
    _In_ uint32_t Param,
    _Inout_ uint32_t* BufferLength,
    _Out_writes_bytes_opt_(*BufferLength)
        void* Buffer
    );

//
// Indicate listener about DoS mode change.
//
_IRQL_requires_max_(DISPATCH_LEVEL)
void
QuicListenerHandleDosModeStateChange(
    _In_ QUIC_LISTENER* Listener,
    _In_ BOOLEAN DosModeEnabled,
    _In_ BOOLEAN OnWorker
    );

//
// Allows the listener to drain some operations that it currently has
// queued up. Returns TRUE if there are still work to do after the function
// returns.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN
QuicListenerDrainOperations(
    _In_ QUIC_LISTENER* Listener
    );
